diff --git a/test/files/pos/t7200b.scala b/test/files/pos/t7200b.scala new file mode 100644 index 000000000000..9d579c6ef954 --- /dev/null +++ b/test/files/pos/t7200b.scala @@ -0,0 +1,50 @@ +import language.higherKinds + +trait T { + def t = 0 +} +trait Foo { + def coflatMap[A <: T](f: A): A +} + +object O extends Foo { + def coflatMap[A <: T](f: A) = { + val f2 = coflatMap(f) // inferred in 2.9.2 / 2.10.0 as [Nothing] + f2.t // so this does't type check. + f2 + } +} + +// Why? When a return type is inherited, the derived method +// symbol first gets a preliminary type assigned, based on the +// 1) method type of a unique matching super member +// 2) viewed as a member type of the inheritor (to substitute, +// e.g. class type parameters) +// 3) substituted to replace the super-method's type parameters +// with those of the inheritor +// 4) dissected to take just the return type wrapped in thisMethodType(). +// +// In Scala 2.10.0 and earlier, this preliminary method type +// +// 1) [A#11329 <: #3.this.T#7068]( f#11333: A#11329)A#11329 +// 2) [A#11329 <: #3.this.T#7068]( f#11333: A#11329)A#11329 +// 3) ( f#12556: A#11336)A#11336 +// 4) [A#11336 <: #3.this.T#7068]( f#12552: A#11337&0)A#11336 +// +// The type #4 from the old version is problematic: the parameter is typed with +// a skolem for the type parameter `A`. It won't be considered to match the +// method it overrides, instead they are seen as being overloaded, and type inference +// goes awry (Nothing is inferred as the type argument for the recursive call +// to coflatMap. +// +// The Namers patch adds one step here: it subsitutes the type parameter symbols +// for the skolems: +// +// https://github.com/scala/scala/commit/b74c33eb#L2R1014 +// +// So we end up with a method symbol info: +// +// 5) [A#11336 <: #3.this.T#7068]( f#12505: A#11336)A#11336 +// +// This *does* match the method in the super class, and type inference +// chooses the correct type argument. \ No newline at end of file diff --git a/test/files/run/t7200.scala b/test/files/run/t7200.scala new file mode 100644 index 000000000000..ba342df14dff --- /dev/null +++ b/test/files/run/t7200.scala @@ -0,0 +1,34 @@ +import language.higherKinds + +object Test 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 + +}