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

-Ydelambdafy:method + lambda taking/returning a value class = kaboom

    Details

      Description

      Under -Ydelambdafy:method, an anonymous function returning a value class will erroneously be translated to an anonymous function returning the underlying type of that value class, instead of its boxed representation, resulting in ClassCastException at runtime.

      DelambdafyBug.scala
      object DelambdafyBug {
        def main(args: Array[String]): Unit = {
          val f = (s: String) => new scala.collection.immutable.StringOps(s)
          f("Hello") foreach println
        }
      }
      
      build.sbt
      scalaVersion := "2.11.0-M7"
      
      scalacOptions += "-Ydelambdafy:method"
      

      During `posterasure`, the knowledge that the result type was a value class, here StringOps (and not a String) is lost. I think `posterasure` should perform a boxing operation, here (or `erasure` itself).

      After erasure:

      after erasure
      package <empty>{type} {
        object DelambdafyBug extends Object {
          def <init>(): DelambdafyBug.type = {
            DelambdafyBug.super{Object}.<init>{()Object}(){Object};
            (){Unit}
          }{Unit};
          def main(args: Array[String]): Unit = {
            val f: Function1 = {
              final <artifact> def $anonfun(s: String): ErasedValueType(class StringOps, String) = s{ErasedValueType(class StringOps, String)};
              ((s: String) => $anonfun{(s: String)ErasedValueType(class StringOps, String)}(s{String}){ErasedValueType(class StringOps, String)}){String => ErasedValu
      eType(class StringOps, String)}.$asInstanceOf{[T0]()T0}[Function1]{()Function1}(){Function1}
            }{Function1};
            f.apply{(v1: Object)Object}("Hello"{String("Hello")}){Object}.$asInstanceOf{[T0]()T0}[collection.IndexedSeqOptimized]{()collection.IndexedSeqOptimized}(){
      collection.IndexedSeqOptimized}.foreach{(f: Function1)Unit}({
              {
                final <artifact> def $anonfun(x: Object): Unit = scala.this{type}.Predef.println{(x: Object)Unit}(x{Object}){Unit};
                ((x: Object) => $anonfun{(x: Object)Unit}(x{Object}){Unit}){Object => Unit}.$asInstanceOf{[T0]()T0}[Function1]{()Function1}(){Function1}
              }{Function1}
            }{Function1}){Unit}
          }{Unit}
        }
      }
      

      After posterasure:

      after posterasure
      package <empty>{type} {
        object DelambdafyBug extends Object {
          def <init>(): DelambdafyBug.type = {
            DelambdafyBug.super{Object}.<init>{()Object}(){Object};
            (){Unit}
          }{Unit};
          def main(args: Array[String]): Unit = {
            val f: Function1 = {
              final <artifact> def $anonfun(s: String): String = s{String};
              ((s: String) => $anonfun{(s: String)String}(s{String}){String}){String => String}.$asInstanceOf{[T0]()T0}[Function1]{()Function1}(){Function1}
            }{Function1};
            f.apply{(v1: Object)Object}("Hello"{String("Hello")}){Object}.$asInstanceOf{[T0]()T0}[collection.IndexedSeqOptimized]{()collection.IndexedSeqOptimized}(){
      collection.IndexedSeqOptimized}.foreach{(f: Function1)Unit}({
              {
                final <artifact> def $anonfun(x: Object): Unit = scala.this{type}.Predef.println{(x: Object)Unit}(x{Object}){Unit};
                ((x: Object) => $anonfun{(x: Object)Unit}(x{Object}){Unit}){Object => Unit}.$asInstanceOf{[T0]()T0}[Function1]{()Function1}(){Function1}
              }{Function1}
            }{Function1}){Unit}
          }{Unit}
        }
      }
      

      If the lambda takes a value class instead instead, then the compiler crashes during `icode` because some ErasedValueType(class StringOps, String) survive until then.

      DelambdafyBug.scala
      object DelambdafyBug {
        def main(args: Array[String]): Unit = {
          val g = (s: scala.collection.immutable.StringOps) => s foreach println
          g("Hello")
        }
      }
      

      After erasure:

      after erasure
      package <empty>{type} {
        object DelambdafyBug extends Object {
          def <init>(): DelambdafyBug.type = {
            DelambdafyBug.super{Object}.<init>{()Object}(){Object};
            (){Unit}
          }{Unit};
          def main(args: Array[String]): Unit = {
            val g: Function1 = {
              final <artifact> def $anonfun(s: ErasedValueType(class StringOps, String)): Unit = new collection.immutable.StringOps{collection.immutable.StringOps}{(r
      epr: String)scala.collection.immutable.StringOps}(s.$asInstanceOf{[T0]()T0}[String]{()String}(){String}){scala.collection.immutable.StringOps}.foreach{(f: Funct
      ion1)Unit}({
                {
                  final <artifact> def $anonfun(x: Object): Unit = scala.this{type}.Predef.println{(x: Object)Unit}(x{Object}){Unit};
                  ((x: Object) => $anonfun{(x: Object)Unit}(x{Object}){Unit}){Object => Unit}.$asInstanceOf{[T0]()T0}[Function1]{()Function1}(){Function1}
                }{Function1}
              }{Function1}){Unit};
              ((s: ErasedValueType(class StringOps, String)) => $anonfun{(s: ErasedValueType(class StringOps, String))Unit}(s{ErasedValueType(class StringOps, String)
      }){Unit}){ErasedValueType(class StringOps, String) => Unit}.$asInstanceOf{[T0]()T0}[Function1]{()Function1}(){Function1}
            }{Function1};
            g.apply{(v1: Object)Object}(new collection.immutable.StringOps{collection.immutable.StringOps}{(repr: String)scala.collection.immutable.StringOps}(scala.t
      his{type}.Predef.augmentString{(x: String)ErasedValueType(class StringOps, String)}("Hello"{String("Hello")}){ErasedValueType(class StringOps, String)}.$asInsta
      nceOf{[T0]()T0}[String]{()String}(){String}){scala.collection.immutable.StringOps}){Object};
            val f: Function1 = {
              final <artifact> def $anonfun(s: String): ErasedValueType(class StringOps, String) = s{ErasedValueType(class StringOps, String)};
              ((s: String) => $anonfun{(s: String)ErasedValueType(class StringOps, String)}(s{String}){ErasedValueType(class StringOps, String)}){String => ErasedValu
      eType(class StringOps, String)}.$asInstanceOf{[T0]()T0}[Function1]{()Function1}(){Function1}
            }{Function1};
            f.apply{(v1: Object)Object}("Hello"{String("Hello")}){Object}.$asInstanceOf{[T0]()T0}[collection.IndexedSeqOptimized]{()collection.IndexedSeqOptimized}(){
      collection.IndexedSeqOptimized}.foreach{(f: Function1)Unit}({
              {
                final <artifact> def $anonfun(x: Object): Unit = scala.this{type}.Predef.println{(x: Object)Unit}(x{Object}){Unit};
                ((x: Object) => $anonfun{(x: Object)Unit}(x{Object}){Unit}){Object => Unit}.$asInstanceOf{[T0]()T0}[Function1]{()Function1}(){Function1}
              }{Function1}
            }{Function1}){Unit}
          }{Unit}
        }
      }
      

      After posterasure:

      after posterasure
      package <empty>{type} {
        object DelambdafyBug extends Object {
          def <init>(): DelambdafyBug.type = {
            DelambdafyBug.super{Object}.<init>{()Object}(){Object};
            (){Unit}
          }{Unit};
          def main(args: Array[String]): Unit = {
            val g: Function1 = {
              final <artifact> def $anonfun(s: String): Unit = new collection.immutable.StringOps{collection.immutable.StringOps}{(repr: String)scala.collection.immut
      able.StringOps}(s{String}){scala.collection.immutable.StringOps}.foreach{(f: Function1)Unit}({
                {
                  final <artifact> def $anonfun(x: Object): Unit = scala.this{type}.Predef.println{(x: Object)Unit}(x{Object}){Unit};
                  ((x: Object) => $anonfun{(x: Object)Unit}(x{Object}){Unit}){Object => Unit}.$asInstanceOf{[T0]()T0}[Function1]{()Function1}(){Function1}
                }{Function1}
              }{Function1}){Unit};
              ((s: String) => $anonfun{(s: String)Unit}(s{String}){Unit}){String => Unit}.$asInstanceOf{[T0]()T0}[Function1]{()Function1}(){Function1}
            }{Function1};
            g.apply{(v1: Object)Object}(new collection.immutable.StringOps{collection.immutable.StringOps}{(repr: String)scala.collection.immutable.StringOps}(scala.t
      his{type}.Predef.augmentString{(x: String)String}("Hello"{String("Hello")}){String}){scala.collection.immutable.StringOps}){Object};
            val f: Function1 = {
              final <artifact> def $anonfun(s: String): String = s{String};
              ((s: String) => $anonfun{(s: String)String}(s{String}){String}){String => String}.$asInstanceOf{[T0]()T0}[Function1]{()Function1}(){Function1}
            }{Function1};
            f.apply{(v1: Object)Object}("Hello"{String("Hello")}){Object}.$asInstanceOf{[T0]()T0}[collection.IndexedSeqOptimized]{()collection.IndexedSeqOptimized}(){
      collection.IndexedSeqOptimized}.foreach{(f: Function1)Unit}({
              {
                final <artifact> def $anonfun(x: Object): Unit = scala.this{type}.Predef.println{(x: Object)Unit}(x{Object}){Unit};
                ((x: Object) => $anonfun{(x: Object)Unit}(x{Object}){Unit}){Object => Unit}.$asInstanceOf{[T0]()T0}[Function1]{()Function1}(){Function1}
              }{Function1}
            }{Function1}){Unit}
          }{Unit}
        }
      }
      

      I suspect that both errors have the same cause, namely that posterasure fails to deal with value classes in Function nodes. Hence I report the two in only one issue.

      Note that both snippets work perfectly fine with -Ydelambdafy:inline (the default).

        Issue Links

          Activity

          Show
          Jason Zaugg added a comment - WIP: https://github.com/retronym/scala/compare/ticket/8017
          Show
          Jason Zaugg added a comment - https://github.com/scala/scala/pull/3274

            People

            • Assignee:
              Jason Zaugg
              Reporter:
              Sébastien Doeraene
            • Votes:
              0 Vote for this issue
              Watchers:
              4 Start watching this issue

              Dates

              • Created:
                Updated:
                Resolved:

                Development