Uploaded image for project: 'Scala Programming Language'
  1. Scala Programming Language
  2. SI-5629

Endless recursion as a result of incorrect specialization

    Details

      Description

      test.scala

      object test extends App {
       
        trait MyPF[@specialized(Int) -A] extends (A => Unit) {
          def isDefinedAt(x: A): Boolean
          def applyOrElse[A1 <: A](x: A1, default: A1 => Unit): Unit = {
            println("MyPF.applyOrElse entered...")
            if (isDefinedAt(x)) apply(x) else default(x)
          }
        }
       
        trait MySmartPF[@specialized(Int) -A] extends MyPF[A] {
          def apply(x: A): Unit = {
            println("MySmartPF.apply entered...")
            applyOrElse(x, { _: Any => throw new MatchError })
          }
        }
       
        type T = Int
        //type T = Any
       
        def newPF(test: T): MyPF[T] = new MySmartPF[T] {
          def isDefinedAt(x: T): Boolean = x != test
          override def applyOrElse[A1 <: T](x: A1, default: A1 => Unit): Unit = {
            println("newPF.applyOrElse entered...")
            if (x != test) { println("ok"); () } else { println("default"); default(x) } 
          }
        }
        
        val pf = newPF(1)
        println("=== pf(1):")
        try { pf(1) } catch { case x => println(x) }
        println("=== pf(42):")
        pf(42)
        println("=== done")
      }
      

      Expected output:

      === pf(1):
      MySmartPF.apply entered...
      newPF.applyOrElse entered...
      default
      scala.MatchError: () (of class scala.runtime.BoxedUnit)
      === pf(42):
      MySmartPF.apply entered...
      newPF.applyOrElse entered...
      ok
      === done
      

      Actual output:

      === pf(1):
      MySmartPF.apply entered...
      MyPF.applyOrElse entered...
      scala.MatchError: () (of class scala.runtime.BoxedUnit)
      === pf(42):
      MySmartPF.apply entered...
      MyPF.applyOrElse entered...
      MySmartPF.apply entered...
      MyPF.applyOrElse entered...
      ...
      java.lang.StackOverflowError
      

      The bug is no longer reproduced when we replace `type T = Int` by `type T = Any` or remove @specialized.

      Now this prevents partial functions to be specialized.

        Attachments

          Activity

          Hide
          d_m Erik Osheim added a comment -

          Given this solution, and thinking about it some more, maybe the correct resolution would be to warn (or error) when a superclass' method (with a specialized type parameter) is overridden with a method that is not specialized?

          It seems clear that you either need to override bar and bar$mcI$sp or neither. The other option would be for the subclass' bar to be automatically specialized, but that seems a bit risky. On the other hand it would certainly be more convenient.

          Anyone else have any thoughts on this?

          Show
          d_m Erik Osheim added a comment - Given this solution, and thinking about it some more, maybe the correct resolution would be to warn (or error) when a superclass' method (with a specialized type parameter) is overridden with a method that is not specialized? It seems clear that you either need to override bar and bar$mcI$sp or neither. The other option would be for the subclass' bar to be automatically specialized, but that seems a bit risky. On the other hand it would certainly be more convenient. Anyone else have any thoughts on this?
          Hide
          prokopec Aleksandar Prokopec added a comment -

          I think the problem here is that `IntChild` overrides `bar`, but does not actually override `bar$mcI$sp`.

          Show
          prokopec Aleksandar Prokopec added a comment - I think the problem here is that `IntChild` overrides `bar`, but does not actually override `bar$mcI$sp`.
          Hide
          prokopec Aleksandar Prokopec added a comment -

          In fact, `bar` in `IntChild` should forward to `bar$mcI$sp`, instead of containing the implementation.

          Show
          prokopec Aleksandar Prokopec added a comment - In fact, `bar` in `IntChild` should forward to `bar$mcI$sp`, instead of containing the implementation.
          Hide
          prokopec Aleksandar Prokopec added a comment -

          The problem was that detecting if a method needs a special override used unification to determine this, and unification didn't used to unify over method type parameters and their type bounds - it simply ignored them.

          Show
          prokopec Aleksandar Prokopec added a comment - The problem was that detecting if a method needs a special override used unification to determine this, and unification didn't used to unify over method type parameters and their type bounds - it simply ignored them.
          Show
          retronym Jason Zaugg added a comment - See: https://issues.scala-lang.org/browse/SI-4123?focusedCommentId=57497&page=com.atlassian.jira.plugin.system.issuetabpanels:comment-tabpanel#comment-57497

            People

            • Assignee:
              prokopec Aleksandar Prokopec
              Reporter:
              ppavlov Pavel Pavlov
            • Votes:
              0 Vote for this issue
              Watchers:
              7 Start watching this issue

              Dates

              • Created:
                Updated:
                Resolved: