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.

        Activity

        Hide
        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
        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
        Aleksandar Prokopec added a comment -

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

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

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

        Show
        Aleksandar Prokopec added a comment - In fact, `bar` in `IntChild` should forward to `bar$mcI$sp`, instead of containing the implementation.
        Hide
        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
        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
        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:
            Aleksandar Prokopec
            Reporter:
            Pavel Pavlov
          • Votes:
            0 Vote for this issue
            Watchers:
            7 Start watching this issue

            Dates

            • Created:
              Updated:
              Resolved:

              Development