Description
This is another case where avoid
is wrong (see #750, #741, #711, and I can probably come up with more contrived examples too):
class M
class A extends M
class B extends M
class Foo[T](x: T) {
def foo[S >: T](other: S): Foo[S] = ???
}
object C {
def xy() = {
val x: Foo[A] = new Foo(new A)
x.foo(new B)
}
}
This compiles but fails with -Ycheck:all
because the block inside the method xy()
is assigned the type Foo[A | B]
but the expression x.foo(new B)
is assigned the type Foo[M]
:
final module class C$() extends Object() { this: C.type =>
def xy(): Foo[A | B] = {
val x: Foo[A] = new Foo[A](new A())
x.foo[M](new B())
}
}
The issue is that in TypeAssigner#avoid
, when we see the type parameter S >: x.T | B
we replace it by A | B
(see TypeAssigner.scala#L90-L93), but in TypeVar#interpolate
we instantiate S
to M
because its upper bound is not a union-type (see Types.scala#L2457-L2461).
@odersky : in #750 you said "Maybe we should instantiate those type variables that have escaping refs as bounds", this would also solve this issue. Note that this issue is currently blocking me from experimenting with improving type inference by removing/reducing the use of interpolateUndetVars
.