Skip to content
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

Incorrect variable arguments to WrappedArray converting #7268

Open
scabug opened this issue Mar 19, 2013 · 7 comments
Open

Incorrect variable arguments to WrappedArray converting #7268

scabug opened this issue Mar 19, 2013 · 7 comments
Labels
Milestone

Comments

@scabug
Copy link

scabug commented Mar 19, 2013

Variable arguments will be converted to WrappedArray implicitly with proper component type, but under below case, for ArrayWrapper.updateOne method, the argument will be converted to WrappedArray$ofRef (should be WrappedArray$ofDouble) discarding the declared type parameter [T] (Double here, for example).

The code below is similar to Scala's collections library code, and works under Scala 2.9.x.

import scala.collection.mutable.WrappedArray
import scala.reflect.ClassTag

object Main {
  
  def main(args: Array[String]) {
    val test = new ArrayWrapper[Double]
    test.update(1.0)    // ok
    test.updateOne(1.0) // failure with ArrayStoreExcpetion
  }
}

class ArrayWrapper[T: ClassTag] {
  val array = new Array[T](2)
  
  // elem will be boxed to java.lang.Object (java.lang.Double), then wrapped as 
  // Array[Object] and passed to scala.LowPriorityImplicits.genericWrapArray[T](xs: Array[T])
  // becaues of update(elems: T*), and causes ArrayStoreException.
  def updateOne(elem: T) {
    update(elem)
  }
  
  // ok when called outside directly
  def update(elems: T*) {
    updateAll(elems)
  }
  
  def updateAll(elems: scala.collection.Traversable[T]) {
    println(elems.getClass)
    // class scala.collection.mutable.WrappedArray$ofDouble -- when via update
    // class scala.collection.mutable.WrappedArray$ofRef    -- when via updateOne
    
    elems match {
      case xs: WrappedArray[T] =>
        println(xs.array) 
        // [D@504ad009                  --- when via update
        // [Ljava.lang.Object;@3b87bd31 --- when via updateOne
        
        array(0) = xs.array(0) 
        // ok
        
        System.arraycopy(xs.array, 0, array, 0, xs.length) 
        // ok             --- when via update
        // failure with java.lang.ArrayStoreException --- when via updateOne
        
        println("== ok ==")
      case _ =>
    }
  }
}
@scabug
Copy link
Author

scabug commented Mar 19, 2013

Imported From: https://issues.scala-lang.org/browse/SI-7268?orig=1
Reporter: @dcaoyuan
Affected Versions: 2.10.1

@scabug
Copy link
Author

scabug commented Mar 19, 2013

@retronym said:
The same ArrayStoreException is generated under 2.9.

% scalac29 -Xprint:jvm -unchecked test/files/run/t7268.scala && scala29 Test
test/files/run/t7268.scala:34: warning: non variable type-argument T in type pattern scala.collection.mutable.WrappedArray[T] is unchecked since it is eliminated by erasure
      case xs: WrappedArray[T] =>
               ^
[[syntax trees at end of jvm]]// Scala source: t7268.scala
package <empty> {
  final object Test extends java.lang.Object with ScalaObject {
    def main(args: Array[java.lang.String]): Unit = {
      val test: ArrayWrapper = new ArrayWrapper(reflect.this.Manifest.Double());
      test.update(scala.this.Predef.wrapDoubleArray(Array[Double]{1.0}));
      test.updateOne(scala.Double.box(1.0))
    };
    def this(): object Test = {
      Test.super.this();
      ()
    }
  };
  class ArrayWrapper extends java.lang.Object with ScalaObject {
    private[this] val array: java.lang.Object = _;
    <stable> <accessor> def array(): java.lang.Object = ArrayWrapper.this.array;
    def updateOne(elem: java.lang.Object): Unit = ArrayWrapper.this.update(scala.this.Predef.genericWrapArray(Array[java.lang.Object]{elem}));
    def update(elems: Seq): Unit = ArrayWrapper.this.updateAll(elems);
    def updateAll(elems: Traversable): Unit = {
      scala.this.Predef.println(elems.getClass());
      {
        <synthetic> val temp1: Traversable = elems;
        if (temp1.$isInstanceOf[scala.collection.mutable.WrappedArray]())
          {
            <synthetic> val temp2: scala.collection.mutable.WrappedArray = temp1.$asInstanceOf[scala.collection.mutable.WrappedArray]();
            val xs: scala.collection.mutable.WrappedArray = temp2;
            scala.this.Predef.println(xs.array());
            runtime.this.ScalaRunTime.array_update(ArrayWrapper.this.array(), 0, runtime.this.ScalaRunTime.array_apply(xs.array(), 0));
            java.this.lang.System.arraycopy(xs.array(), 0, ArrayWrapper.this.array(), 0, xs.length());
            scala.this.Predef.println("== ok ==")
          }
        else
          {
            ()
          }
      }
    };
    def this(implicit evidence$1: scala.reflect.ClassManifest): ArrayWrapper = {
      ArrayWrapper.super.this();
      ArrayWrapper.this.array = evidence$1.newArray(2);
      ()
    }
  }
}

one warning found

class scala.collection.mutable.WrappedArray$ofDouble
[D@518f5824
== ok ==
class scala.collection.mutable.WrappedArray$ofRef
[Ljava.lang.Object;@19e3cd51
java.lang.ArrayStoreException
	at java.lang.System.arraycopy(Native Method)
	at ArrayWrapper.updateAll(t7268.scala:42)
	at ArrayWrapper.update(t7268.scala:25)
	at ArrayWrapper.updateOne(t7268.scala:20)
	at Test$.main(t7268.scala:9)
	at Test.main(t7268.scala)
	at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
	at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)
	at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
	at java.lang.reflect.Method.invoke(Method.java:597)
	at scala.tools.nsc.util.ScalaClassLoader$$anonfun$run$1.apply(ScalaClassLoader.scala:78)
	at scala.tools.nsc.util.ScalaClassLoader$class.asContext(ScalaClassLoader.scala:24)
	at scala.tools.nsc.util.ScalaClassLoader$URLClassLoader.asContext(ScalaClassLoader.scala:88)
	at scala.tools.nsc.util.ScalaClassLoader$class.run(ScalaClassLoader.scala:78)
	at scala.tools.nsc.util.ScalaClassLoader$URLClassLoader.run(ScalaClassLoader.scala:101)
	at scala.tools.nsc.ObjectRunner$.run(ObjectRunner.scala:33)
	at scala.tools.nsc.ObjectRunner$.runAndCatch(ObjectRunner.scala:40)
	at scala.tools.nsc.MainGenericRunner.runTarget$1(MainGenericRunner.scala:56)
	at scala.tools.nsc.MainGenericRunner.process(MainGenericRunner.scala:80)
	at scala.tools.nsc.MainGenericRunner$.main(MainGenericRunner.scala:89)
	at scala.tools.nsc.MainGenericRunner.main(MainGenericRunner.scala)

% cat test/files/run/t7268.scala import scala.collection.mutable.WrappedArray
//import scala.reflect.ClassTag

object Test {

  def main(args: Array[String]) {
    val test = new ArrayWrapper[Double]
    test.update(1.0)    // ok
    test.updateOne(1.0) // failure with ArrayStoreExcpetion
  }
}

class ArrayWrapper[T: ClassManifest] {
  val array = new Array[T](2)

  // elem will be boxed to java.lang.Object (java.lang.Double), then wrapped as
  // Array[Object] and passed to scala.LowPriorityImplicits.genericWrapArray[T](xs: Array[T])
  // becaues of update(elems: T*), and causes ArrayStoreException.
  def updateOne(elem: T) {
    update(elem)
  }

  // ok when called outside directly
  def update(elems: T*) {
    updateAll(elems)
  }

  def updateAll(elems: scala.collection.Traversable[T]) {
    println(elems.getClass)
    // class scala.collection.mutable.WrappedArray$ofDouble -- when via update
    // class scala.collection.mutable.WrappedArray$ofRef    -- when via updateOne

    elems match {
      case xs: WrappedArray[T] =>
        println(xs.array)
        // [D@504ad009                  --- when via update
        // [Ljava.lang.Object;@3b87bd31 --- when via updateOne

        array(0) = xs.array(0)
        // ok

        System.arraycopy(xs.array, 0, array, 0, xs.length)
        // ok             --- when via update
        // failure with java.lang.ArrayStoreException --- when via updateOne

        println("== ok ==")
      case _ =>
    }
  }
}

The difference is that 2.10 doesn't issue the unchecked pattern match warning.

@scabug
Copy link
Author

scabug commented Apr 1, 2013

@Sciss said:
I think this is related:

// File: build.sbt
scalaVersion := "2.10.1"

// File: src/main/scala/bug/Bug.scala
package bug

class Foo(val args: Any*)
case class Bar(id: Int) extends Foo(id)

object Bug extends App {
  val msg = Bar(666)
  val id  = msg.id // boom!
}

This produces the following runtime error (sbt run):

[error] (run-main) java.lang.ClassCastException: 
  scala.collection.mutable.WrappedArray$ofRef cannot be cast to java.lang.Integer
java.lang.ClassCastException: scala.collection.mutable.WrappedArray$ofRef cannot be 
  cast to java.lang.Integer
    at scala.runtime.BoxesRunTime.unboxToInt(Unknown Source)
    at bug.Bar.id(Test.scala:4)
    at bug.Bug$delayedInit$body.apply(Test.scala:8)
    at scala.Function0$class.apply$mcV$sp(Function0.scala:40)
        ...

The problem disappears when I change back to Scala 2.10.0.

@scabug
Copy link
Author

scabug commented May 25, 2013

@retronym said:
@Sciss I think your bug is #7436

@scabug
Copy link
Author

scabug commented Aug 23, 2013

@Sciss said:
@retronym – I don't think it is #7436, because that says it's fixed in Scala 2.10.3-RC1. I am just running my example with Scala 2.10.3-RC1 and it still crashes. This affects some core libraries I am using (https://github.com/Sciss/ScalaOSC and https://github.com/Sciss/ScalaCollider). It is a little sad that they only work properly with 2.10.0. Any chance of getting this fixed in the 2.10 series?

@scabug
Copy link
Author

scabug commented Aug 22, 2014

@dcaoyuan said:
Any update?

@szeiger
Copy link
Member

szeiger commented Jul 31, 2019

For the record, here is an updated version that compiles on 2.13:

import scala.collection.immutable.ArraySeq
import scala.reflect.ClassTag

object Test {

  def main(args: Array[String]): Unit = {
    val test = new ArrayWrapper[Double]
    test.update(1.0)    // ok
    test.updateOne(1.0) // failure with ArrayStoreExcpetion
  }
}

class ArrayWrapper[T: ClassTag] {
  val array = new Array[T](2)

  // elem will be boxed to java.lang.Object (java.lang.Double), then wrapped as
  // Array[Object] and passed to scala.LowPriorityImplicits.genericWrapArray[T](xs: Array[T])
  // becaues of update(elems: T*), and causes ArrayStoreException.
  def updateOne(elem: T): Unit = {
    update(elem)
  }

  // ok when called outside directly
  def update(elems: T*): Unit = {
    updateAll(elems)
  }

  def updateAll(elems: scala.collection.Traversable[T]): Unit = {
    println(elems.getClass)
    // class scala.collection.mutable.WrappedArray$ofDouble -- when via update
    // class scala.collection.mutable.WrappedArray$ofRef    -- when via updateOne

    elems match {
      case xs: ArraySeq[T] =>
        println(xs.unsafeArray)
        // [D@504ad009                  --- when via update
        // [Ljava.lang.Object;@3b87bd31 --- when via updateOne

        array(0) = xs.unsafeArray(0).asInstanceOf[T]
        // ok

        System.arraycopy(xs.unsafeArray, 0, array, 0, xs.length)
        // ok             --- when via update
        // failure with java.lang.ArrayStoreException --- when via updateOne

        println("== ok ==")
      case _ =>
    }
  }
}

The behavior is still the same. I'm inclined to say that it's not a bug (you get an ArraySeq.ofRef which contains an Array[Object]; use one of Scala's array copying methods instead of the "untyped" System.arraycopy and everything works just fine). But the fact that new Array[T] does make use of the available ClassTag[T] whereas the varargs handling code does not is an unexpected inconsistency.

The second issue appears to be fixed.

@scala scala deleted a comment from scabug Jan 20, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

No branches or pull requests

3 participants