bug icon indicating copy to clipboard operation
bug copied to clipboard

Wrong (misleading) error in code that is (non-obviously) incorrect

Open scabug opened this issue 14 years ago • 7 comments

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?

scabug avatar Nov 07 '11 09:11 scabug

Imported From: https://issues.scala-lang.org/browse/SI-5159?orig=1 Reporter: Ben Wing (benwing)

scabug avatar Nov 07 '11 09:11 scabug

@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
                ^

scabug avatar Nov 07 '11 09:11 scabug

@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.

scabug avatar Nov 07 '11 09:11 scabug

@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 {
      ^

scabug avatar Nov 07 '11 09:11 scabug

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

som-snytt avatar Jul 31 '20 06:07 som-snytt

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

SethTisue avatar Feb 01 '24 21:02 SethTisue

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.

som-snytt avatar Feb 02 '24 09:02 som-snytt