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

reflection behaves badly with respect to value class fields

    Details

      Description

      Reflection understands correctly the type of value-class fields, but the instance mirrors return values of the wrong type when accessing these fields:

      import scala.reflect.runtime._
      
      case class Foo(x: Int) extends AnyVal
      case class Bar(foo: Foo)
      
      object Test {
        def main(args: Array[String]) {
          val fooTerm = universe.typeOf[Bar].declaration(universe.newTermName("foo")).asMethod
          val fooType = fooTerm.returnType // correctly gives typeOf[Foo]
          println(fooType) // Foo
      
          val bar = Bar(Foo(3))
          println(bar.foo) // Foo(3)
      
          val im = currentMirror.reflect(bar)
      
          val obj1 = im.reflectField(fooTerm).get // incorrectly gives java.lang.Integer(3) not Foo(3)
          println(obj1) // 3
      
          val obj2 = im.reflectMethod(fooTerm)() // ditto
          println(obj2) // 3
        }
      }
      

        Activity

        Hide
        Eugene Burmako added a comment -

        Workaround, courtesy of Jeff Olson:

          def getFieldValue(im: InstanceMirror, term: MethodSymbol): Any = {
            val rawValue = im.reflectMethod(term)()
            val tpe = term.returnType
            val symbol = tpe.typeSymbol.asClass
            if (symbol.isDerivedValueClass) {
              val ctor = tpe.declaration(nme.CONSTRUCTOR).asMethod
              val ctorMirror = mirror.reflectClass(symbol).reflectConstructor(ctor)
              ctorMirror(rawValue)
            }
            else rawValue
          }
        
        Show
        Eugene Burmako added a comment - Workaround, courtesy of Jeff Olson: def getFieldValue(im: InstanceMirror, term: MethodSymbol): Any = { val rawValue = im.reflectMethod(term)() val tpe = term.returnType val symbol = tpe.typeSymbol.asClass if (symbol.isDerivedValueClass) { val ctor = tpe.declaration(nme.CONSTRUCTOR).asMethod val ctorMirror = mirror.reflectClass(symbol).reflectConstructor(ctor) ctorMirror(rawValue) } else rawValue }
        Hide
        Jeff Olson added a comment -

        This may be obvious, but setters are broken too:

        import scala.reflect.runtime.universe._
        
        case class Foo(n: Int) extends AnyVal
        class Bar(var foo: Foo)
        
        object Test {
          def main(args: Array[String]) {
            val bar = new Bar(Foo(3))
        
            val mirror = runtimeMirror(getClass.getClassLoader)
            val im = mirror.reflect(bar)
            val fooTerm = typeOf[Bar].declaration(newTermName("foo")).asTerm
            val fooField = im.reflectField(fooTerm)
            fooField.set(Foo(5)) // java.lang.IllegalArgumentException: Can not set int field Bar.foo to Foo
            fooField.set(5) // this works but probably shouldn't
            println(bar.foo)
          }
        }
        
        Show
        Jeff Olson added a comment - This may be obvious, but setters are broken too: import scala.reflect.runtime.universe._ case class Foo(n: Int) extends AnyVal class Bar(var foo: Foo) object Test { def main(args: Array[String]) { val bar = new Bar(Foo(3)) val mirror = runtimeMirror(getClass.getClassLoader) val im = mirror.reflect(bar) val fooTerm = typeOf[Bar].declaration(newTermName("foo")).asTerm val fooField = im.reflectField(fooTerm) fooField.set(Foo(5)) // java.lang.IllegalArgumentException: Can not set int field Bar.foo to Foo fooField.set(5) // this works but probably shouldn't println(bar.foo) } }
        Hide
        Jeff Olson added a comment -

        And constructors as well. However, in this case, there doesn't seem to be a workaround (at least that I've found so far):

        
        import scala.reflect.runtime.universe._
        
        case class Foo(n: Int) extends AnyVal
        case class Bar(foo: Foo)
        
        object Test { 
          def main(args: Array[String]) {
            val mirror = runtimeMirror(getClass.getClassLoader)
            val cm = mirror.reflectClass(typeOf[Bar].typeSymbol.asClass)
            val ctor = typeOf[Bar].declaration(nme.CONSTRUCTOR).asMethod
            val ctorm = cm.reflectConstructor(ctor)
            ctorm(Foo(3)) // java.lang.NoClassDefFoundError: no Java class corresponding to ErasedValueType(Foo) found
            ctorm(3)      // java.lang.NoClassDefFoundError: no Java class corresponding to ErasedValueType(Foo) found
          }
        }
        
        Show
        Jeff Olson added a comment - And constructors as well. However, in this case, there doesn't seem to be a workaround (at least that I've found so far): import scala.reflect.runtime.universe._ case class Foo(n: Int) extends AnyVal case class Bar(foo: Foo) object Test { def main(args: Array[String]) { val mirror = runtimeMirror(getClass.getClassLoader) val cm = mirror.reflectClass(typeOf[Bar].typeSymbol.asClass) val ctor = typeOf[Bar].declaration(nme.CONSTRUCTOR).asMethod val ctorm = cm.reflectConstructor(ctor) ctorm(Foo(3)) // java.lang.NoClassDefFoundError: no Java class corresponding to ErasedValueType(Foo) found ctorm(3) // java.lang.NoClassDefFoundError: no Java class corresponding to ErasedValueType(Foo) found } }
        Hide
        James Iry added a comment -

        2.10.2 is about to be cut. Kicking down the road and un-assigning to foster work stealing.

        Show
        James Iry added a comment - 2.10.2 is about to be cut. Kicking down the road and un-assigning to foster work stealing.
        Hide
        Eugene Burmako added a comment -

        The thing with constructors is https://issues.scala-lang.org/browse/SI-6411, which has just been fixed in a pull request.

        Show
        Eugene Burmako added a comment - The thing with constructors is https://issues.scala-lang.org/browse/SI-6411 , which has just been fixed in a pull request.
        Show
        Eugene Burmako added a comment - https://github.com/scala/scala/pull/3409

          People

          • Assignee:
            Eugene Burmako
            Reporter:
            Jeff Olson
          • Votes:
            0 Vote for this issue
            Watchers:
            6 Start watching this issue

            Dates

            • Created:
              Updated:
              Resolved:

              Development