scala / bug

Scala 2 bug reports only. Please, no questions — proper bug reports only.
https://scala-lang.org
232 stars 21 forks source link

Generic overloads possible in java not possible in scala #2936

Open scabug opened 14 years ago

scabug commented 14 years ago

I'm creating this ticket to document the issue and my effort to address it, as I expect it to come up again, and I think it's probably fixable with more effort.

Consider the following:

trait Selector
trait Closeable
trait Disposable
trait DatagramSocket

class X {
  def disposes[T <: Selector](abstractSelector: T) { }
  def disposes[T <: Closeable](closeable: T) { }
  def disposes[T <: Disposable](disposable: T) { }
  def disposes(datagramSocket: DatagramSocket): DatagramSocket = datagramSocket
}

This is rejected by scalac in the typer with:

generic-overload.scala:9: error: method disposes is defined twice
  def disposes[T <: Disposable](disposable: T) { }
      ^
one error found

However equivalent java source can be compiled. The post-erasure method signatures are all distinct so in theory scala also should be able to compile this.

Attempting to address this I adjusted the compiler logic. The method which needs a relaxed variation is "matches" in Types.

    def matches(that: Type): Boolean = matchesType(this, that, !phase.erasedTypes)

And then in Typers, "checkNoDoubleDefsAndAddSynthetics" would use the variation, which would treat polytypes as different if their type parameter bounds would lead to different erasures. This is delicate because if it is relaxed too much, test cases such as tcpoly_boundedmonad will fail to compile:

trait Monad[T <: Bound[T], MyType[x <: Bound[x]], Bound[_]] {
  def map[S <: Bound[S]](f: T => S): MyType[S]  // etc
}                                                                               
class Set[T <: Ordered[T]] extends Monad[T, Set, Ordered] {
  def map[S <: Ordered[S]](f: T => S): Set[S] = error("TODO") // etc
}                                                                   

So it has to be relaxed far enough to let the motivating example proceed, but not so far that (for instance) Set is no longer seen as implementing the methods in Monad.

scabug commented 14 years ago

Imported From: https://issues.scala-lang.org/browse/SI-2936?orig=1 Reporter: @paulp

szeiger commented 6 years ago

There is a workaround but it is not clear to me from SLS 5.1.3 whether it is actually legal:

scala> trait T { def f[X](x: X): Unit = () }
defined trait T

scala> trait IsItAnOverload extends T { def f[X <: AnyRef](x: X): Int = 0 }
<console>:12: error: overriding method f in trait T of type [X](x: X)Unit;
 method f needs `override' modifier
       trait IsItAnOverload extends T { def f[X <: AnyRef](x: X): Int = 0 }
                                            ^

scala> trait OrIsItAnOverride extends T { override def f[X <: AnyRef](x: X): Int = 0 }
<console>:12: error: overriding method f in trait T of type [X](x: X)Unit;
 method f has incompatible type
       trait OrIsItAnOverride extends T { override def f[X <: AnyRef](x: X): Int = 0 }
                                                       ^

These two work according to SLS 5.1.3. Equality of argument types is checked after substituting type parameters, therefore differences in type bounds are not taken into account. However:

scala> trait WhatKindOfEquality extends T { def f[X <: AnyRef](x: X with Any): Int = 0 }
defined trait WhatKindOfEquality

This means that X and X with Any (for the same X) are considered different types for the purpose of determining "matching" method types even though they are mutually assignable according to conventional type equality rules:

trait Foo[X] { def x: X; def xa: X with Any; val xToXa: X with Any = x; val xaToX: X = xa }
szeiger commented 6 years ago

And if you think the answer is to use proper type equality rules, consider this:

scala> trait WhatKindOfEquality extends T { def f[X <: AnyRef](x: X with AnyRef): Int = 0 }
defined trait WhatKindOfEquality

Depending on which way you do the substitution, X with AnyRef is or is not equal to X, so you cannot do this correctly after substituting type parameters.

paulp commented 6 years ago

@szeiger "Two compound types are equivalent if the sequences of their component are pairwise equivalent, and occur in the same order, and their refinements are equivalent." This would seem to imply that many types which are sometimes-to-often treated as equivalent are not equivalent by the specification. I see no provision for compound type simplification. They must be "pairwise equivalent", which implies the same number of components.

X with X   =:= X // nope
X with Any =:= X // nope
X with Y   =:= X // nope (this where X <: Y)

An abstract type might expand to multiple components of a compound type, allowing the occasional T1 with T2 =:= T3 with T4 with T5 situation to complete a hail mary for equivalence. To sort it all out, I rely on unofficial guidance.

szeiger commented 6 years ago

This has apparently changed in Dotty. The compound type workaround no longer works there.