0

As far as i understand, @specialized annotation should generate some unboxed code for every primitive type i mentioned, but this doesn't work:

scala> def aaa[@specialized(Int, Double, Float, Long) T] = (5.0).doubleValue.asInstanceOf[T]
aaa: [T]=> T

scala> aaa[Int]
unrecoverable error (inside interpreter/compiler)

This compiles:

scala> def aaa[@specialized(Int, Double, Float, Long) T](a: T) = (5.0).doubleValue.asInstanceOf[T]
aaa: [T]=> T

scala> aaa[Int](0)
ClassCastException

But it still uses boxed type for asInstanceOf[T]. This obviously works:

scala> (5.0).asInstanceOf[Int]
res28: Int = 5

UPDATE: Type erasure and answers like that Writing a generic cast function Scala has nothing to do with my problem. Type erasure just preventing compiler from adding typecast byte-code operation for generics, but eventually it will be added - see ClassCastException (generated by this op) in my REPL

Community
  • 1
  • 1
dk14
  • 22,206
  • 4
  • 51
  • 88

3 Answers3

3

The problem was in Scala REPL - @specialized doesn't work there. Compiling def aaa[@specialized(Int) T] = (5.0).asInstanceOf[T] with scalac gives:

  public <T> T aaa();
    Code:
       0: ldc2_w        #15                 // double 5.0d
       3: invokestatic  #22                 // Method scala/runtime/BoxesRunTime
.boxToDouble:(D)Ljava/lang/Double;
       6: areturn

  public int aaa$mIc$sp();
    Code:
       0: ldc2_w        #15                 // double 5.0d
       3: d2i
       4: ireturn

d2i is exactly what i was expecting. And of course everything works fine with scalac (so I don't need pattern matching for every possible type). So it's just the issue with interpreter.

dk14
  • 22,206
  • 4
  • 51
  • 88
  • 1
    For it's solution to http://stackoverflow.com/questions/27212776/scala-cast-to-generic-type-for-generic-numerical-function +1 – sema Dec 01 '14 at 10:50
1

The method is specialized, but under separate compilation (i.e., different lines), the specialized method isn't invoked.

In the following, b.B.f works, c.B.f is broken.

$ scala -Xprint:typer,cleanup

scala> :pa -raw
// Entering paste mode (ctrl-D to finish)

package a { object A { def aaa[@specialized(Int) T] = (5.0).doubleValue.asInstanceOf[T] }}
package b { object B { def f = a.A.aaa[Int] }}

// Exiting paste mode, now interpreting.

[[syntax trees at end of                     typer]] // <pastie>
package <empty> {
  package a {
    object A extends scala.AnyRef {
      def <init>(): a.A.type = {
        A.super.<init>();
        ()
      };
      def aaa[@specialized(scala.Int) T]: T = scala.this.Predef.double2Double(5.0).doubleValue().asInstanceOf[T]
    }
  };
  package b {
    object B extends scala.AnyRef {
      def <init>(): b.B.type = {
        B.super.<init>();
        ()
      };
      def f: Int = a.A.aaa[Int]
    }
  }
}

[[syntax trees at end of                   cleanup]] // <pastie>
package <empty> {
  package a {
    object A extends Object {
      def aaa(): Object = scala.Double.box(scala.this.Predef.double2Double(5.0).doubleValue());
      <specialized> def aaa$mIc$sp(): Int = scala.this.Predef.double2Double(5.0).doubleValue().toInt();
      def <init>(): a.A.type = {
        A.super.<init>();
        ()
      }
    }
  };
  package b {
    object B extends Object {
      def f(): Int = a.A.aaa$mIc$sp();
      def <init>(): b.B.type = {
        B.super.<init>();
        ()
      }
    }
  }
}


scala> :pa -raw
// Entering paste mode (ctrl-D to finish)

package c { object B { def f = a.A.aaa[Int] }}

// Exiting paste mode, now interpreting.

[[syntax trees at end of                     typer]] // <pastie>
package c {
  object B extends scala.AnyRef {
    def <init>(): c.B.type = {
      B.super.<init>();
      ()
    };
    def f: Int = a.A.aaa[Int]
  }
}

[[syntax trees at end of                   cleanup]] // <pastie>
package c {
  object B extends Object {
    def f(): Int = scala.Int.unbox(a.A.aaa());
    def <init>(): c.B.type = {
      B.super.<init>();
      ()
    }
  }
}
som-snytt
  • 39,429
  • 2
  • 47
  • 129
  • so if i call specialized method from another unit - it will not work even with scalac? – dk14 Dec 03 '14 at 14:56
  • @dk14 the REPL is a resident compiler, so the bug probably originates there. Maybe it doesn't even see the specialized signatures, only the original symbol; that's just a guess. – som-snytt Dec 05 '14 at 06:06
-1

Perhaps you just need a more recent compiler? here's what I get with 2.11.2:

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

scala> def aaa[@specialized(Int, Double, Float, Long) T] = (5.0).doubleValue.asInstanceOf[T]
aaa: [T]=> T

scala> aaa[Int]
java.lang.ClassCastException: java.lang.Double cannot be cast to java.lang.Integer
  at scala.runtime.BoxesRunTime.unboxToInt(BoxesRunTime.java:105)
  ... 33 elided

scala> aaa[Float]
java.lang.ClassCastException: java.lang.Double cannot be cast to java.lang.Float
  at scala.runtime.BoxesRunTime.unboxToFloat(BoxesRunTime.java:113)
  ... 33 elided

scala> aaa[Double]
res2: Double = 5.0

scala> 5.0.asInstanceOf[Int]
res3: Int = 5

scala> 5.0.asInstanceOf[Integer]
java.lang.ClassCastException: java.lang.Double cannot be cast to java.lang.Integer
  ... 33 elided

Note that you can directly do 5.0.asInstanceOf[Int] but not 5.0.asInstanceOf[Integer]. Also note that the ClassCastException that you get with aaa[Int] is referring to java.lang.Integer not Scala's Int class. I suspect what goes on here is that the @specialized annotation is generating a function, aaa, that has an "Int" specialization but because of type erasure and boxing is converting that to java.lang.Integer and there is no automatic conversion with asInstanceOf between Double and Integer.

Reid Spencer
  • 2,776
  • 28
  • 37
  • I use latest compiler, but maybe it's REPL specific problem. Anyway the problem is that even with `@specialized` annotation type inside function is still **boxed** (Integer instead of Int). There is no type-erasure in compile-time - more than that every generated function definetely knows about type - so there is no reason to not specify directly Int instead of boxed Integer. – dk14 Nov 30 '14 at 16:21