From 8703e00b3876cad8750abf82949737cac8bde6fe Mon Sep 17 00:00:00 2001 From: Jason Zaugg Date: Tue, 26 Mar 2013 09:39:01 +0100 Subject: SI-7200 Test case for fixed type inference error. 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)); { - val x$1: Nothing = r.head; - r.tail.::[Nothing](x$1) + val x$1: B = r.head; + r.tail.::[B](x$1) } } })) b74c33eb86 represents the exact moment of progression. Comments in pos/t7200b.scala, a minimal test that demonstrates the problem without type constructors or code execution, pinpoint the line of code responsible for the fix. Incidentally, I'm currently on a train somewhere between Solothurn and Biel, and am consequently without the power of scala-bisector. Undeterred, and inspired by a line I saw in Skyfall last night ("sometimes the olds ways are better"), I just pulled off a two-hop bisection. Take that, O(log N)! The one remaining worry is the appearance of the type variable ?A1 in the output of -Xprint:typer for run/t7200.scala. --- test/files/pos/t7200b.scala | 50 +++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 50 insertions(+) create mode 100644 test/files/pos/t7200b.scala (limited to 'test/files/pos') diff --git a/test/files/pos/t7200b.scala b/test/files/pos/t7200b.scala new file mode 100644 index 0000000000..9d579c6ef9 --- /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 -- cgit v1.2.3