Scala Programming Language
  1. Scala Programming Language
  2. SI-4437

long-standing spec issues with asInstanceOf

    Details

    • Type: Bug Bug
    • Status: Closed
    • Priority: Major Major
    • Resolution: Fixed
    • Affects Version/s: None
    • Fix Version/s: None
    • Component/s: Specification
    • Labels:

      Description

      1) Primitives.

      This is one of those things which makes our lives harder for no benefit that I can see.

      The test x.asInstanceOf[T] is treated specially if T is a numeric value type (�12.2).
      In this case the cast will be translated to an application of a conversion method x.toT (�12.2.1).
      For non-numeric values x the operation will raise a ClassCastException.
      

      It has nice pointy teeth (this one is SI-1448):

      scala> 5.asInstanceOf[Long]
      res0: Long = 5
      
      scala> (5: AnyVal).asInstanceOf[Long]
      java.lang.ClassCastException: java.lang.Integer cannot be cast to java.lang.Long
      	at scala.runtime.BoxesRunTime.unboxToLong(Unknown Source)
      	at .<init>(<console>:8)
      

      This sort of thing just seems wrong:

      scala> class A1 { def f[T <: Int](x: T) = x.toLong }                    
      defined class A1
      
      scala> class A2 { def f[T <: Int](x: T) = x.asInstanceOf[Long] }
      defined class A2
      
      scala> (new A1) f 5
      res1: Long = 5
      
      scala> (new A2) f 5
      java.lang.ClassCastException: java.lang.Integer cannot be cast to java.lang.Long
      	at scala.runtime.BoxesRunTime.unboxToLong(Unknown Source)
      
      scala> class A3[T] { def f(x: T) = x.asInstanceOf[Long] }
      defined class A3
      
      scala> class A4[@specialized T] { def f(x: T) = x.asInstanceOf[Long] }
      defined class A4
      
      scala> (new A3[Int]) f 5
      java.lang.ClassCastException: java.lang.Integer cannot be cast to java.lang.Long
      	at scala.runtime.BoxesRunTime.unboxToLong(Unknown Source)
      
      scala> (new A4[Int]) f 5
      res1: Long = 5
      
      

      But if it can only be used successfully on the final numeric types and not AnyVal and Any, what advantage does it have over 5.toLong?

      I don't see how it makes conceptual sense either. A cast suggests that the value in question already has the correct representation, and the type system only needs to be informed that it's a B in addition to being an A. But casting an Int to a Long clearly requires a conversion because they have different sizes. And indeed it is specified as applying a conversion.

      So what are we getting out of this self-identified "treated specially"-case which we wouldn't have by deprecating-and-eliminating it?

      2) Null.

      The spec says:

      • asInstanceOf[T ] returns the “null” object itself if T conforms to scala.AnyRef, 
      and throws a NullPointerException otherwise.
      A reference to any other member of the “null” object causes a NullPointerException to be thrown.
      

      This is not true, and cannot reasonably be made true at present. Example usages in trunk include:

      // JavaConversions
      override def get(key: AnyRef): B = try {
        underlying get key.asInstanceOf[A] match {
          case None => null.asInstanceOf[B]
          case Some(v) => v
        }
      } catch {
        case ex: ClassCastException => null.asInstanceOf[B]
      }
      // UnrolledBuffer, chosen for the pithy comment
      while (idx < until) {
        array(idx) = null.asInstanceOf[T] // TODO find a way to assign a default here!!
        idx += 1
      }
      

      For reasons I don't know, the syntax "var x: T = _" only works for fields, not locals. Without it there is no way to initialize a generic type unless you have a pre-existing value or are willing to cast null in defiance of the spec. So by default, null.asInstanceOf[T] has become the "best practice" way to initialize such values (given that it's the only way.) We need to either endorse it (and change the spec) or offer an alternative and bring the spec into line with reality. Right now there is a "shadow spec" on this point.

      My suggestion is to just endorse it, since in practice I don't think we can change it. Even if "var x: T = _" were enabled for local declarations, SI-602 gets into the real problem. If you parameterize a java type on a primitive, it will come back full of nulls, because in reality it's holding boxes. If nulls are treated as uninitialized values of the right type (i.e. 0 or false) then things work. If not then they won't. I don't think that situation can be finessed.

        Issue Links

          Activity

          Hide
          Martin Odersky added a comment -

          We need to leave asInstanceOf as an operation that can cast an Any to a numeric type. Old code (including in the compiler itself) relies on it. We should investigate whether the cases with a ClassCastException can be fixed without too much cost.

          Agreed on (2). Let's endorse it. Reassigning to Paul for the time being, but feel free to pull other people in as needed.

          Show
          Martin Odersky added a comment - We need to leave asInstanceOf as an operation that can cast an Any to a numeric type. Old code (including in the compiler itself) relies on it. We should investigate whether the cases with a ClassCastException can be fixed without too much cost. Agreed on (2). Let's endorse it. Reassigning to Paul for the time being, but feel free to pull other people in as needed.
          Hide
          Simon Ochsenreither added a comment -

          The pull request for spec update is https://github.com/scala/scala-dist/pull/20.

          Show
          Simon Ochsenreither added a comment - The pull request for spec update is https://github.com/scala/scala-dist/pull/20 .
          Hide
          Grzegorz Kossakowski added a comment -

          Unassigning and rescheduling to M7 as previous deadline was missed.

          Show
          Grzegorz Kossakowski added a comment - Unassigning and rescheduling to M7 as previous deadline was missed.
          Hide
          Simon Ochsenreither added a comment -

          Spec update for issue number 2: https://github.com/scala/scala-dist/commit/804c390adf62fd4380b29861cdcc3c35d6194093

          Shall we close this bug and move issue 1 to a new bug number?

          Show
          Simon Ochsenreither added a comment - Spec update for issue number 2: https://github.com/scala/scala-dist/commit/804c390adf62fd4380b29861cdcc3c35d6194093 Shall we close this bug and move issue 1 to a new bug number?
          Hide
          Adriaan Moors added a comment -

          See SI-1448 for related compiler work.

          Show
          Adriaan Moors added a comment - See SI-1448 for related compiler work.

            People

            • Assignee:
              Simon Ochsenreither
              Reporter:
              Paul Phillips
              TracCC:
              Paul Phillips, Seth Tisue
            • Votes:
              1 Vote for this issue
              Watchers:
              8 Start watching this issue

              Dates

              • Created:
                Updated:
                Resolved:

                Development