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

long-standing spec issues with asInstanceOf #4437

Closed
scabug opened this issue Apr 2, 2011 · 5 comments
Closed

long-standing spec issues with asInstanceOf #4437

scabug opened this issue Apr 2, 2011 · 5 comments
Assignees

Comments

@scabug
Copy link

scabug commented Apr 2, 2011

  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 #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?

  1. Null.

The spec says:

asInstanceOf[T ] returns the “nullobject itself if T conforms to scala.AnyRef, 
and throws a NullPointerException otherwise.
A reference to any other member of the “nullobject 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, #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.

@scabug
Copy link
Author

scabug commented Apr 2, 2011

Imported From: https://issues.scala-lang.org/browse/SI-4437?orig=1
Reporter: @paulp

@scabug
Copy link
Author

scabug commented Apr 12, 2011

@odersky said:
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.

@scabug
Copy link
Author

scabug commented Nov 18, 2012

@soc said:
The pull request for spec update is scala/scala-dist#20.

@scabug
Copy link
Author

scabug commented Oct 22, 2013

@soc said:
Spec update for issue number 2: scala/scala-dist@804c390

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

@scabug
Copy link
Author

scabug commented Mar 11, 2014

@adriaanm said:
See #1448 for related compiler work.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

2 participants