Open scabug opened 14 years ago
Imported From: https://issues.scala-lang.org/browse/SI-2936?orig=1 Reporter: @paulp
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 }
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.
@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.
This has apparently changed in Dotty. The compound type workaround no longer works there.
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:
This is rejected by scalac in the typer with:
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.
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:
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.