possible compiler bug

79 views
Skip to first unread message

cs...@broadinstitute.org

unread,
Sep 25, 2015, 9:06:44 PM9/25/15
to scala-internals
I think I might have found a compiler bug.  I'm running 2.11.7.

I have the following code which splits an array into an array of arrays before any element that satisfies the predicate `p`. It type checks:

    def splitBefore[T](a: Array[T], p: (T) => Boolean)(implicit tct: ClassTag[T]): Array[Array[T]] =
      a.foldLeft(Array[Array[T]](Array.empty[T])) {
(acc: Array[Array[T]], s: T) => if (p(s))
 acc :+ Array(s)
else
 acc.init :+ (acc.last :+ s)
    }

It works fine when I call it with non-empty `a`:

    scala> splitBefore(Array("a", "BC", "d"), (s: String) => s.size > 1)
    res1: Array[Array[String]] = Array(Array(a), Array(BC, d))

But when I call it with an empty array, I get a `ClassCastException`:

    scala> splitBefore(Array.empty[String], (s: String) => s.size > 1)
    java.lang.ClassCastException: [Ljava.lang.Object; cannot be cast to [[Ljava.lang.String;
      ... 33 elided

When I hand-inline the call so there is no type parameterization, it works fine:

    scala> Array().foldLeft(Array(Array.empty[String])) {
         |   (acc: Array[Array[String]], s: String) => if (s.size > 1)
         |     acc :+ Array(s)
         |   else
         |     acc.init :+ (acc.last :+ s)
         | }
    res1: Array[Array[String]] = Array(Array())

Best,
Cotton

Simon Schäfer

unread,
Sep 26, 2015, 1:36:23 AM9/26/15
to scala-internals
On Saturday, September 26, 2015 at 3:06:44 AM UTC+2, cs...@broadinstitute.org wrote:
I think I might have found a compiler bug.  I'm running 2.11.7.

I have the following code which splits an array into an array of arrays before any element that satisfies the predicate `p`. It type checks:

    def splitBefore[T](a: Array[T], p: (T) => Boolean)(implicit tct: ClassTag[T]): Array[Array[T]] =
      a.foldLeft(Array[Array[T]](Array.empty[T])) {
(acc: Array[Array[T]], s: T) => if (p(s))
 acc :+ Array(s)
else
 acc.init :+ (acc.last :+ s)
    }

It works fine when I call it with non-empty `a`:

    scala> splitBefore(Array("a", "BC", "d"), (s: String) => s.size > 1)
    res1: Array[Array[String]] = Array(Array(a), Array(BC, d))

But when I call it with an empty array, I get a `ClassCastException`:

    scala> splitBefore(Array.empty[String], (s: String) => s.size > 1)
    java.lang.ClassCastException: [Ljava.lang.Object; cannot be cast to [[Ljava.lang.String;
      ... 33 elided

Can't reproduce. Does it happen for you in a clean REPL?
 

Roman Janusz

unread,
Sep 26, 2015, 5:48:49 AM9/26/15
to scala-internals
Here's a simpler case:

Welcome to Scala version 2.11.7 (Java HotSpot(TM) 64-Bit Server VM, Java 1.8.0_60).
Type in expressions to have them evaluated.
Type :help for more information.

scala> import scala.reflect.ClassTag
import scala.reflect.ClassTag

scala> def arr[T: ClassTag]: Array[Array[T]] = Array[Array[T]](null)
arr: [T](implicit evidence$1: scala.reflect.ClassTag[T])Array[Array[T]]

scala> arr[String]

java.lang.ClassCastException: [Ljava.lang.Object; cannot be cast to [[Ljava.lang.String;
  ... 33 elided
Message has been deleted

Roman Janusz

unread,
Sep 26, 2015, 5:59:28 AM9/26/15
to scala-internals
The bytecode looks just wrong:

         0: iconst_1
         1: anewarray     #4                  // class java/lang/Object
         4: dup
         5: iconst_0
         6: aconst_null
         7: aastore
         8: checkcast     #16                 // class "[Ljava/lang/Object;"
        11: areturn

It should use the class tag to create the array through reflection.

Vlad Ureche

unread,
Sep 26, 2015, 7:00:03 PM9/26/15
to scala-internals

On Sat, Sep 26, 2015 at 11:59 AM, Roman Janusz <romeqj...@gmail.com> wrote:


It should use the class tag to create the array through reflection.

Definitely a bug. The cleanup phase is stripping away the outer array:

[[syntax trees at end of              constructors]] // arr.scala
package <empty> {
  object Test extends Object {
    def arr(implicit evidence$1: scala.reflect.ClassTag): Array[Object] =
      scala.Array.apply(scala.this.Predef.wrapRefArray(Array[Object]{null}), (ClassTag.apply(ScalaRunTime.this.arrayClass(evidence$1.runtimeClass())): scala.reflect.ClassTag)).$asInstanceOf[Array[Object]]();
    def <init>(): Test.type = {
      Test.super.<init>();
      ()
    }
  }
}

[[syntax trees at end of                   flatten]] // arr.scala: tree is unchanged since constructors
[[syntax trees at end of                     mixin]] // arr.scala: tree is unchanged since constructors
[[syntax trees at end of                   cleanup]] // arr.scala
package <empty> {
  object Test extends Object {
    def arr(implicit evidence$1: scala.reflect.ClassTag): Array[Object] = Array[Object]{null}.$asInstanceOf[Array[Object]]();
    def <init>(): Test.type = {
      Test.super.<init>();
      ()
    }
  }
}

Can you file it on the tracker?

HTH,
Vlad

Reply all
Reply to author
Forward
0 new messages