Wrong (misleading) error in code that is (non-obviously) incorrect
Compile the following:
// testinherit.scala
abstract class A {
type Foo
def bar(foo: Foo): Unit
}
class BFoo {
}
class CFoo extends BFoo {
}
class B extends A {
type Foo = BFoo
def bar(foo: Foo) = {
println("B called")
}
}
class C extends B {
override type Foo = CFoo
override def bar(foo: Foo) = {
super.bar(foo)
println("C called")
}
}
Result when compiling:
$ scalac testinherit.scala
testinherit.scala:22: error: ambiguous reference to overloaded definition,
both method bar in class B of type (foo: C.this.Foo)Unit
and method bar in class A of type (foo: C.this.Foo)Unit
match argument types (C.this.Foo)
super.bar(foo)
^
one error found
This error is, at the very least, extremely cryptic.
I encountered this recently and eventually figured out that I couldn't quite do what I was trying to do in code like the above. Rather, I have to write class B like this:
abstract class B extends A {
override type Foo <: BFoo
def bar(foo: Foo) {
println("B called")
}
}
However, it took me a long time to figure this out. The above error simply makes no sense whatsoever, because class A is totally abstract and class B is a subclass of A -- how could there possibly be an overload clash between a given class and its completely abstract parent?
Imported From: https://issues.scala-lang.org/browse/SI-5159?orig=1 Reporter: Ben Wing (benwing)
@paulp said: The problem is that the error you see halts compilation before the error you should see is ever emitted, that being what you see if you comment out the override of bar in C:
./a.scala:23: error: overriding type Foo in class B, which equals BFoo;
type Foo has incompatible type
override type Foo = CFoo
^
@paulp said: "how could there possibly be an overload clash between a given class and its completely abstract parent?"
It's easy, because it doesn't matter what is abstract. Overloading resolution does not ignore abstract methods - little would work correctly if it did. Since the Foo in A and the Foo in B are different types, you arrive at an ambiguous overload pretty quickly.
@paulp said: Oh, and that's communicated via the other helpful error it shows if you comment out bar in C:
./a.scala:22: error: class C needs to be abstract, since method bar in class A of type (foo: C.this.Foo)Unit is not defined
(Note that A.this.Foo does not match B.this.Foo)
class C extends B {
^
Probably one would never think to ask it, but:
$ scalac -Yuntil:refchecks -Vdebug test/files/neg/t5159.scala
[running phase parser on t5159.scala]
[running phase namer on t5159.scala]
[running phase packageobjects on t5159.scala]
[running phase typer on t5159.scala]
test/files/neg/t5159.scala:20: error: ambiguous reference to overloaded definition,
both method bar in class B of type (foo: C.this.Foo): scala.this.Unit
and method bar in class A of type (foo: C.this.Foo): scala.this.Unit
match argument types (C.this.Foo)
super.bar(foo)
^
[running phase superaccessors on t5159.scala]
test/files/neg/t5159.scala:20: error: [ suppressed ] super may not be used on value <error>; super can only be used to select a member that is a method or type
super.bar(foo)
^
warning: !!! Mismatch trying to zip method parameters and argument list:
params = List()
args = List(foo)
[running phase extmethods on t5159.scala]
[running phase pickler on t5159.scala]
[running phase refchecks on t5159.scala]
test/files/neg/t5159.scala:18: error: incompatible type in overriding
type Foo = <empty>.this.BFoo (defined in class B)
(Equivalent type required when overriding a type alias.)
override type Foo = CFoo
^
1 warning
2 errors
As with other suppressed and hidden things, it would be nicer to see a summary:
1 suppressed warning in superaccessors
1 future error in refchecks
Scala 3 (3.4.0-RC3) does a better job:
21 | override type Foo = CFoo
| ^
| error overriding type Foo in class B, which equals BFoo;
| type Foo, which equals CFoo has incompatible type
Scala 2 sees an overload because findMember checks type equality of parameters, which compares the underlying types (which are wrong). Using typeSymbolDirect to notice they must "match" is closer, but test fails where two methods taking List differ in return type.