Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Improperly inferred return type for recursive calls in abstract method implementation #7200

scabug opened this issue Mar 1, 2013 · 3 comments


Copy link

@scabug scabug commented Mar 1, 2013

Snippet below fails with ClassCastException at runtime.

import language.higherKinds

object FooExample extends App {

  // Slice of comonad is where this came up
  trait Foo[F[_]] {
    def coflatMap[A, B](f: F[A] => B): F[A] => F[B]

  // A non-empty list
  case class Nel[A](head: A, tail: List[A])

  object NelFoo extends Foo[Nel] {
    // It appears that the return type for recursive calls is not inferred
    // properly, yet no warning is issued. Providing a return type or
    // type arguments for the recursive call fixes the problem.
    def coflatMap[A, B](f: Nel[A] => B) = // ok w/ return type
      l => Nel(f(l), l.tail match {
        case Nil => Nil
        case h :: t => {
          val r = coflatMap(f)(Nel(h, t)) // ok w/ type args
          r.head :: r.tail

  // Without a recursive call all is well, but with recursion we get a 
  // ClassCastException from Integer to Nothing
  NelFoo.coflatMap[Int, Int](_.head + 1)(Nel(1, Nil)) // Ok
  NelFoo.coflatMap[Int, Int](_.head + 1)(Nel(1, List(2))) // CCE

Copy link

@scabug scabug commented Mar 1, 2013

Imported From:
Reporter: Rob Norris (rnorris)
Affected Versions: 2.10.0

Copy link

@scabug scabug commented Mar 26, 2013

@retronym said:
This bug has gone away in 2.10.1.


Broken in 2.9.2 and 2.10.0, but working in 2.10.1

--- sandbox/2.10.0.log
+++ sandbox/2.10.1.log
       def coflatMap[A >: Nothing <: Any, B >: Nothing <: Any](f: Test.Nel[A] => B): Test.Nel[A] => Test.Nel[B] = ((l: Test.Nel[A]) => Test.this.Nel.apply[B](f.apply(l), l.tail match {
         case immutable.this.Nil => immutable.this.Nil
-        case (hd: A, tl: List[A])scala.collection.immutable.::[A]((h @ _), (t @ _)) => {
-          val r: Test.Nel[Nothing] = NelFoo.this.coflatMap[A, Nothing](f).apply(Test.this.Nel.apply[A](h, t));
+        case (hd: A, tl: List[A])scala.collection.immutable.::[?A1]((h @ _), (t @ _)) => {
+          val r: Test.Nel[B] = NelFoo.this.coflatMap[A, B](f).apply(Test.this.Nel.apply[A](h, t));
-            <synthetic> val x$1: Nothing = r.head;
-            r.tail.::[Nothing](x$1)
+            <synthetic> val x$1: B = r.head;
+            r.tail.::[B](x$1)

b74c33e represents the exact moment of progression. We should
investigate that a little further so we can fully connect the dots.

But that diff is a bit troublesome: what it the TypeVar ?A1 doing in the post-typer tree? I will pull on that thread to see what I can find.

I chatted to Lukas today about his patch to Namers that (ostensibly) fixed this, and it seems to me that our current scheme for allowing recursive methods to use inherited inferred return types is quite risky.

scala> trait A { def foo: Any }; object B extends A { def foo = {if(false) foo; "" } };
defined trait A
defined module B
res0: String = ""

During typechecking of B#foo, the method has the return type Any, which is later refined to String.
We can see this by trying to ascribe the more specific type:

scala> trait A { def foo: Any }; object B extends A { def foo = {if(false) foo: String; "" } };
<console>:9: error: type mismatch;
 found   : Any
 required: String
              trait A { def foo: Any }; object B extends A { def foo = {if(false) foo: String; "" } };;

Copy link

@scabug scabug commented Mar 26, 2013

@retronym said:
Marking as fixed in 2.10.1; test case and analysis here:


@scabug scabug closed this Apr 20, 2013
@scabug scabug added the infer label Apr 7, 2017
@scabug scabug added this to the 2.10.1 milestone Apr 7, 2017
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Linked pull requests

Successfully merging a pull request may close this issue.

None yet
2 participants