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

Boom! you stepped on a bug with specialization and companion objects

    Details

    • Type: Bug Bug
    • Status: Closed
    • Priority: Major Major
    • Resolution: Fixed
    • Affects Version/s: Scala 2.9.1
    • Fix Version/s: Scala 2.10.0-M4
    • Component/s: Specialization
    • Labels:
    • Environment:

      os/x, apple jdk

      Description

      I came across the following situation.

      object Run {
       def main(args:Array[String]) {
        val a = Blarg(Array(1,2,3))
        println(a.m((x:Int) => x+1))
       }
      }
      
      object Blarg {
        def apply[T:Manifest](a:Array[T]) = new Blarg(a)
      }
      class Blarg [@specialized T:Manifest](val a:Array[T]) {
        def m[@specialized W>:T,@specialized S](f:W=>S) = f(a(0))
      }
      
      

      When run, this throws the runtime exception
      java.lang.RuntimeException: boom! you stepped on a bug. This method should never be called.

      However, when we specialize the apply method as follows,

      object Blarg {
        def apply[@specialized T:Manifest](a:Array[T]) = new Blarg(a)
      }
      

      everything goes through as it should.

      The type parameters used in m are a workaround for the lack
      of specialization on result type I submitted earlier.

        Activity

        Hide
        Marc Millstone added a comment -

        I should add that this is dependent on the specialization of the method m, if we remove that
        everything goes through as it should.

        Show
        Marc Millstone added a comment - I should add that this is dependent on the specialization of the method m, if we remove that everything goes through as it should.
        Hide
        Paul Phillips added a comment -

        With a small variation I made my way to a NoSuchMethodError instead. Noting for future fixer.

        
        object Run {
          def main(args:Array[String]) {
            println( Blarg(Array(1)) m ((x: Int) => x + 1) )
          }
        }
        
        object Blarg {
          def apply[T: Manifest](a: Array[T]) = new Blarg(a)
        }
        class Blarg[T](val a:Array[T]) {
          // java.lang.NoSuchMethodError: Blarg.m$mIIc$sp(Lscala/Function1;)I
          //  at Run$.main(a.scala:6)
          //  at Run.main(a.scala)
          def m[@specialized W >: T, @specialized S](f: W => S) = f(a(0))
        }
        
        Show
        Paul Phillips added a comment - With a small variation I made my way to a NoSuchMethodError instead. Noting for future fixer. object Run { def main(args:Array[String]) { println( Blarg(Array(1)) m ((x: Int) => x + 1) ) } } object Blarg { def apply[T: Manifest](a: Array[T]) = new Blarg(a) } class Blarg[T](val a:Array[T]) { // java.lang.NoSuchMethodError: Blarg.m$mIIc$sp(Lscala/Function1;)I // at Run$.main(a.scala:6) // at Run.main(a.scala) def m[@specialized W >: T, @specialized S](f: W => S) = f(a(0)) }
        Hide
        Marc Millstone added a comment -

        Great.

        Thanks Paul. I am really deep into specialization and please let me know
        how else I can help.

        Show
        Marc Millstone added a comment - Great. Thanks Paul. I am really deep into specialization and please let me know how else I can help.
        Hide
        Paul Phillips added a comment -

        As you have clearly discovered, specialization leaks like a sieve as soon as you get away from simple, non-compositional uses. I wish I could propose a realistic form of assistance you could provide, but unless you're interested in learning the compiler well enough to tackle the implementation (and it's not exactly the easiest part of the compiler) then for the moment we're both waiting for a white knight: although I would love to work on specialization, it's of necessity way down on my list of priorities.

        Show
        Paul Phillips added a comment - As you have clearly discovered, specialization leaks like a sieve as soon as you get away from simple, non-compositional uses. I wish I could propose a realistic form of assistance you could provide, but unless you're interested in learning the compiler well enough to tackle the implementation (and it's not exactly the easiest part of the compiler) then for the moment we're both waiting for a white knight: although I would love to work on specialization, it's of necessity way down on my list of priorities.
        Hide
        Aleksandar Prokopec added a comment -

        In the example where `T` on `Blarg` is not specialized, the compiler issues a warning about the lower bound of `W`.
        I believe it should issue an error instead - the specialized method cannot be generated with `T` as a lower bound.

        I'm experimenting on what would happen if a method with a `T` as a lower bound really got specialized.

        Show
        Aleksandar Prokopec added a comment - In the example where `T` on `Blarg` is not specialized, the compiler issues a warning about the lower bound of `W`. I believe it should issue an error instead - the specialized method cannot be generated with `T` as a lower bound. I'm experimenting on what would happen if a method with a `T` as a lower bound really got specialized.
        Hide
        Marc Millstone added a comment -

        I did not see this warning with 2.9.1-final.

        The only reason this code exists is due to issue https://issues.scala-lang.org/browse/SI-5281
        Where it seems that if class[@specialized T] A

        { def f[@specialized W](a:T):W}

        is not specializing across the cross product of T and W. This was a workaround to force
        specialization across the input and output.

        Show
        Marc Millstone added a comment - I did not see this warning with 2.9.1-final. The only reason this code exists is due to issue https://issues.scala-lang.org/browse/SI-5281 Where it seems that if class [@specialized T] A { def f[@specialized W](a:T):W} is not specializing across the cross product of T and W. This was a workaround to force specialization across the input and output.
        Hide
        Aleksandar Prokopec added a comment -

        In the case where `T` is specialized:
        I think the issue can be solved when by generating the normalized method `m$mIIc$sp` of `m`.
        Instead of throwing an exception in the implementation of `m$mIIc$sp`, the implementation of `m` should be copied so that all occurrences of expressions of type `T` are cast to `Int`. Generally, it would mean that all expressions of type equal to the lower bound of some specialized type parameter are cast to the specialized type of that type parameter.
        This is still type-safe because the method `m` above can only be called if `W = Int >: T`, therefore, it's only callable with such a `W` if the enclosing class is instantiated at `T = Int`. Otherwise, the cast in the normalized method would be invalid, but the method itself wouldn't be callable anyway.
        This would require modifying the `Duplicators` to provide them with a `castMap` which essentially says for which types certain expressions of that type have to be cast, and then insert a cast at these locations.

        Iulian, can you comment on this? Do you agree?

        Show
        Aleksandar Prokopec added a comment - In the case where `T` is specialized: I think the issue can be solved when by generating the normalized method `m$mIIc$sp` of `m`. Instead of throwing an exception in the implementation of `m$mIIc$sp`, the implementation of `m` should be copied so that all occurrences of expressions of type `T` are cast to `Int`. Generally, it would mean that all expressions of type equal to the lower bound of some specialized type parameter are cast to the specialized type of that type parameter. This is still type-safe because the method `m` above can only be called if `W = Int >: T`, therefore, it's only callable with such a `W` if the enclosing class is instantiated at `T = Int`. Otherwise, the cast in the normalized method would be invalid, but the method itself wouldn't be callable anyway. This would require modifying the `Duplicators` to provide them with a `castMap` which essentially says for which types certain expressions of that type have to be cast, and then insert a cast at these locations. Iulian, can you comment on this? Do you agree?
        Show
        Aleksandar Prokopec added a comment - In: https://github.com/scala/scala/pull/755

          People

          • Assignee:
            Aleksandar Prokopec
            Reporter:
            Marc Millstone
          • Votes:
            0 Vote for this issue
            Watchers:
            7 Start watching this issue

            Dates

            • Created:
              Updated:
              Resolved:

              Development