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

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

    Details

    • Type: Bug Bug
    • Status: Closed
    • Priority: Major Major
    • Resolution: Not a Bug
    • Affects Version/s: Scala 2.9.1
    • Fix Version/s: None
    • Component/s: Misc Compiler
    • Labels:

      Description

      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 SI-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'.

      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>

        Activity

        Hide
        Jason Zaugg added a comment -

        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.

        Show
        Jason Zaugg added a comment - 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.
        Hide
        Grzegorz Kossakowski added a comment -

        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 SI-5256.

        Show
        Grzegorz Kossakowski added a comment - 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 SI-5256 .

          People

          • Assignee:
            Unassigned
            Reporter:
            Graham Lea
          • Votes:
            0 Vote for this issue
            Watchers:
            8 Start watching this issue

            Dates

            • Created:
              Updated:
              Resolved:

              Development