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

The Scala compiler breaks java.lang.Field.getGenericType() by erasing type parameters when they are primitive wrappers #5255

Closed
scabug opened this issue Nov 30, 2011 · 3 comments
Labels

Comments

@scabug
Copy link

@scabug scabug commented Nov 30, 2011

The Scala compiler is erasing generic type information of fields that are declared with a parameterised type where the type argument is one of Scala's pseudo-primitive types (i.e. Int, Double, etc.)

This is a showstopper for sophisticated reflection libraries that make heavy use of generic type information, e.g. the 'coercion' component in SodaTest (https://github.com/GrahamLea/SodaTest)

This seems to be a partial regression of #1395.

Scala script:

class OptionTypes {
  val optionInt: Option[Int] = Some(1)
  val optionDouble: Option[Double] = Some(4.5)
  val optionString: Option[String] = Some("")
  val listInt: List[Int] = List(1)
  val listString: List[String] = List("")
}
println(classOf[OptionTypes].getDeclaredField("optionInt").getGenericType)
println(classOf[OptionTypes].getDeclaredField("optionDouble").getGenericType)
println(classOf[OptionTypes].getDeclaredField("optionString").getGenericType)
println(classOf[OptionTypes].getDeclaredField("listInt").getGenericType)
println(classOf[OptionTypes].getDeclaredField("listString").getGenericType)

Run against 2.8.1.final:

scala> class OptionTypes {
     |   val optionInt: Option[Int] = Some(1)
     |   val optionDouble: Option[Double] = Some(4.5)
     |   val optionString: Option[String] = Some("")
     |   val listInt: List[Int] = List(1)
     |   val listString: List[String] = List("")
     | }
defined class OptionTypes

scala> println(classOf[OptionTypes].getDeclaredField("optionInt").getGenericType)
scala.Option<java.lang.Integer>

scala> println(classOf[OptionTypes].getDeclaredField("optionDouble").getGenericType)
scala.Option<java.lang.Double>

scala> println(classOf[OptionTypes].getDeclaredField("optionString").getGenericType)
scala.Option<java.lang.String>

scala> println(classOf[OptionTypes].getDeclaredField("listInt").getGenericType)
scala.collection.immutable.List<java.lang.Integer>

scala> println(classOf[OptionTypes].getDeclaredField("listString").getGenericType)
scala.collection.immutable.List<java.lang.String>

Run against 2.9.1-final:
Note that optionInt, optionDouble and listInt have run time type arguments of 'java.lang.Object'.
{noformat}
scala> class OptionTypes {
| val optionInt: Option[Int] = Some(1)
| val optionDouble: Option[Double] = Some(4.5)
| val optionString: Option[String] = Some("")
| val listInt: List[Int] = List(1)
| val listString: List[String] = List("")
| }
defined class OptionTypes

scala> println(classOf[OptionTypes].getDeclaredField("optionInt").getGenericType)
scala.Option<java.lang.Object>

scala> println(classOf[OptionTypes].getDeclaredField("optionDouble").getGenericType)
scala.Option<java.lang.Object>

scala> println(classOf[OptionTypes].getDeclaredField("optionString").getGenericType)
scala.Option<java.lang.String>

scala> println(classOf[OptionTypes].getDeclaredField("listInt").getGenericType)
scala.collection.immutable.List<java.lang.Object>

scala> println(classOf[OptionTypes].getDeclaredField("listString").getGenericType)
scala.collection.immutable.List<java.lang.String>{noformat}

@scabug

This comment has been minimized.

Copy link
Author

@scabug scabug commented Nov 30, 2011

Imported From: https://issues.scala-lang.org/browse/SI-5255?orig=1
Reporter: Graham Lea (grlea)
Affected Versions: 2.9.1

@scabug

This comment has been minimized.

Copy link
Author

@scabug scabug commented Nov 30, 2011

@retronym said:
This is by design.

http://www.scala-lang.org/node/9157 (CTRL-F: "Scala's Signature Dish, Chapter I".)

I think there are additional ML threads or commit comments that further explain the design tensions, but I can't dig them up right now.

You'll probably be interested in the upcoming reflection capabilities of Scala 2.10.

@scabug scabug closed this Nov 30, 2011
@scabug

This comment has been minimized.

Copy link
Author

@scabug scabug commented Nov 30, 2011

@gkossakowski said:
For the record, the following works if you compile with trunk compiler:

class OptionTypes {
  val optionInt: Option[Int] = Some(1)
  val optionDouble: Option[Double] = Some(4.5)
  val optionString: Option[String] = Some("")
  val listInt: List[Int] = List(1)
  val listString: List[String] = List("")
}

import scala.reflect.mirror._

object Test extends App {
  val tpe = classToType(classOf[OptionTypes])
  def getDeclaredField(tpe: Type, name: String) = 
    tpe.member(newTermName(name))
  println(getDeclaredField(tpe, "optionInt").tpe match { case NullaryMethodType(resultTpe) => resultTpe })
  println(getDeclaredField(tpe, "optionDouble").tpe match { case NullaryMethodType(resultTpe) => resultTpe })
  println(getDeclaredField(tpe, "optionString").tpe match { case NullaryMethodType(resultTpe) => resultTpe })
  println(getDeclaredField(tpe, "listInt").tpe match { case NullaryMethodType(resultTpe) => resultTpe })
  println(getDeclaredField(tpe, "listString").tpe match { case NullaryMethodType(resultTpe) => resultTpe })
}

and gives the output:

Option[Int]
Option[Double]
Option[String]
List[Int]
List[String]

The API for Scala reflection is really awkward (it's actually non-existing) so the code isn't pretty. This will be improved.

Also, this code doesn't work in REPL due to #5256.

@scabug scabug added the erasure label Apr 7, 2017
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
1 participant
You can’t perform that action at this time.